package dp.mobile.entity.tower;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Point;
import android.util.Log;

import dp.mobile.entity.Bullet;
import dp.mobile.entity.Enemy;
import dp.mobile.entity.Entity;
import dp.mobile.entity.generator.Generator;
import dp.mobile.entity.saveable.SaveableTower;
import dp.mobile.game.Player;
import dp.mobile.glwrapper.GLTexture;
import dp.mobile.glwrapper.GameRenderer;
import dp.mobile.system.Utilities;

public class Tower extends Entity implements Serializable {
	private static final String TAG = "Tower";
	private static final long serialVersionUID = 1033528291697302883L;
	protected Tower() {
		//Initialize
		mName			= "Tower";
		mData			= null;
		mTileX			= 0;
		mTileY			= 0;
		mReloadTimer	= 0;
		mTowerAngle		= 0;
		mShowRange		= false;
		mBullets		= new ArrayList<Bullet>();
		mGenerators		= new ArrayList<Generator>();
		mProgressTime 	= 0;
		mCurrentTime 	= 0;
		mActive			= false;
		mGeneratorDamageBonus	= 0;
		mGeneratorSpeedBonus	= 1;
	}

	public Tower(TowerData data, Player player, int x, int y, long startTime, boolean delay) {
		//Default
		this();

		//Save data
		mTileX 	= x;
		mTileY 	= y;
		mData	= data;
		mPlayer	= player;
		mActive	= true;
		
		//upgrade(data, startTime, delay);
	}
	
	public boolean upgrade(TowerData newData, long startTime, boolean delay, boolean force) {
		if (isInProgress(startTime) && !force) return false;
		boolean canBuy = true;
		int[] cost = new int[] {0, 0, 0};
		
		if (mPlayer.getElectricity() < newData.getBuildCost()) canBuy = false;
		// kalau bukan tower basic, maka renggut lah komponennya
		if (mData.getWorth(0) != 0 || mData.getWorth(1) != 0 || mData.getWorth(2) != 0) {
			for (int i = 0; i < 3; ++i) {
				cost[i] = newData.getWorth(i) - mData.getWorth(i);
				
				if (cost[i] > mPlayer.getComponents(i)) {
					canBuy = false;
					//break;
				}
			}
		}
		
		if (!canBuy && !force) return false;
		
		mPlayer.addElectricity(-newData.getBuildCost());
		for (int i = 0; i < 3; ++i)
			mPlayer.addComponent(i, -cost[i]);
		if (delay) {
			/// TODO : upgrade tiap level lebih lama?
			mProgressTime	= newData.getUpgradeTime();
			mStartProgress	= startTime;
		}
		mCurrentTime = startTime;
		mData = newData;
		
		return true;
	}
	
	public boolean dismantle(long startTime, boolean force) {
		if (mPlayer.getElectricity() < mData.getDismantleCost() && !force) return false;
		if (isInProgress(startTime) && !force) return false;
		
		mPlayer.addElectricity(-mData.getDismantleCost());
		mCurrentTime = startTime;
		mStartProgress = startTime;
		/// TODO : ada waktu dismantle?
		mProgressTime = mData.getUpgradeTime();
		
		return true;
	}

	public TowerData getData() {
		return mData;
	}
	
	public int getDamage() {
		return mData.getBullet().getDamage();
	}
	
	public int getBonusDamage() {
		return (int)mGeneratorDamageBonus;
	}

	public void setRangeShowing(boolean showRange) {
		mShowRange = showRange;
	}

	public Bullet[] getBullets() {
		//Initialize
		Bullet[] Bullets = null;

		//If not empty
		if (!mBullets.isEmpty()) {
			//Add bullet
			Bullets = new Bullet[mBullets.size()];
			for (int i = 0; i < mBullets.size(); i++) Bullets[i] = mBullets.get(i);
		}
		mBullets.clear();

		//Return the array
		return Bullets;
	}
	
	public void addGenerator(Generator generator) {
		mGenerators.add(generator);
		mActive = true;
		calculateGeneratorBonus();
		
	}
	
	public void removeGenerator(Generator generator) {
		mGenerators.remove(generator);
		// kalo ga ada generator = mati
		if (mGenerators.size() == 0) mActive = false;
		calculateGeneratorBonus();
	}
	
	private void calculateGeneratorBonus() {
		// calculate damage
		ArrayList<Float> arr = new ArrayList<Float>();
		for (int i = 0; i < mGenerators.size(); ++i)
			arr.add(mGenerators.get(i).getDamageBonus());
		
		Collections.sort(arr, Collections.reverseOrder());
		mGeneratorDamageBonus = 0;
		long mult = 1;
		
		for (int i = 0; i < mGenerators.size(); ++i){
			mGeneratorDamageBonus += arr.get(i) / mult;
			mult *= 2L;
		}
		
		// calculate speed
		arr.clear();
		for (int i = 0; i < mGenerators.size(); ++i)
			arr.add(mGenerators.get(i).getSpeedBonus());
		
		Collections.sort(arr);
		mGeneratorSpeedBonus = 1;
		mult = 1;
		
		for (int i = 0; i < mGenerators.size(); ++i){
			mGeneratorSpeedBonus *= (100.0f + arr.get(i) / mult) / 100.0f;
			mult *= 2L;
		}
		
	}
	
	public boolean isInProgress(long time) {
		return time - mStartProgress <= mProgressTime;
	}
	
	public long getReload(){
		return (long) (mData.getReload() / mGeneratorSpeedBonus);
	}

	public void update(long time, ArrayList<Enemy> enemies) {
		if (!mActive) return;
		mCurrentTime += time;
		
		// shooting
		if (!isInProgress(mCurrentTime)) {
			calculateGeneratorBonus();
			// rotate tower
			if (mLocked != null) {
				Point center = mLocked.getCenter();
				final float dX = center.x	- (getX() + Utilities.TILE_WIDTH / 2);
				final float dY = center.y	- (getY() + + Utilities.TILE_HEIGHT / 2);
				mTowerAngle = (90 - (float)Math.toDegrees(Math.atan2(dY, dX))) % 360;
			}
			//Reduce reload
			if (mReloadTimer > 0) mReloadTimer -= time;
			else {
				//Calculate range
				long TowerRange = mData.getRange() * mData.getRange();
	
				//If locked on enemy exist
				if (mLocked != null) {
					//Is it exist?
					if (enemies.contains(mLocked)) {
						//Check range
						final int X			= mLocked.getX() - getX();
						final int Y			= mLocked.getY() - getY();
						final long Range	= (X * X) + (Y * Y);
						if (Range > TowerRange) {
							mLocked = null;
						}
					} else mLocked = null;
				}
	
				//If there's no locked enemy
				if (mLocked == null) {
					long Closest = -1;
	
					//For each enemy
					for (int i = 0; i < enemies.size(); i++) {
						//Calculate range
						final int X			= enemies.get(i).getX() - getX();
						final int Y			= enemies.get(i).getY() - getY();
						final long Range	= (X * X) + (Y * Y);
	
						//Set as locked if closest
						if ((Closest < 0 || Range < Closest) && enemies.get(i).getPlayer().equals(mPlayer)) {
							Closest = Range;
							mLocked = enemies.get(i);
						}
					}
	
					//Check if locked is in range
					if (mLocked != null) {
						if (Closest > TowerRange) mLocked = null;
					}
				}
	
				//If locked enemy exist
				if (mLocked != null) {
					//Create bullet
					mBullets.add(new Bullet(mData.getBullet(), mLocked, getX() + Utilities.TILE_WIDTH / 2, getY() + Utilities.TILE_HEIGHT / 2, (int)mGeneratorDamageBonus));
	
					//Load next shot
					mReloadTimer += getReload();
				}
			}
		}
	}
	
	@Override
	public void draw(GL10 gl) {
		if (mData.getImage() == null) return;

		//Calculate draw position
		final int X = getX();
		final int Y = getY();
		
		//if (Y - mData.getImage().Height > ((endY + 1) * Utilities.TILE_HEIGHT)) return;
		
		//Draw
		final GLTexture texture = mData.getImage();
		GameRenderer.instance().draw(gl, texture, GameRenderer.FLIPPED_TEXTURE, X, Y, Utilities.TILE_WIDTH, Utilities.TILE_HEIGHT, Utilities.TOWER_DEPTH, mTowerAngle);
		
		// draw progress bar
		if (isInProgress(mCurrentTime)) {
			final long progress = mCurrentTime - mStartProgress;
			GameRenderer.instance().draw(gl, GameRenderer.instance().getProgressBarImage(), X, Y - 6, (int)((float)progress / (float)mProgressTime * (float)(Utilities.TILE_WIDTH)), 4, Utilities.TOWER_DEPTH);
		}
		
		//Draw range
		if (mShowRange) {
			int range = mData.getRange();
			final int rangeX = X + Utilities.TILE_WIDTH / 2 - range;
			final int rangeY = Y + Utilities.TILE_HEIGHT / 2 - range;
			range = range * 2;
			GameRenderer.instance().draw(gl, GameRenderer.instance().getRangeImage(), rangeX, rangeY, range, range, Utilities.RANGE_DEPTH);
		}
	}
	
	public SaveableTower createSaveable() {
		SaveableTower retval = new SaveableTower();
		
		retval.mId = getID();
		retval.mTileX = mTileX;
		retval.mTileY = mTileY;
		retval.mTowerDataIndex = getData().getIndex();
		retval.mAngle = mTowerAngle;
		
		for (Generator g : mGenerators) {
			retval.mGenerators.add(g.getID());
		}
		
		return retval;
	}
	
	public void setAngle(float angle) {
		mTowerAngle = angle;
	}
	
	//Data
	protected TowerData mData;
	protected float		mTowerAngle;

	// delaying stuff
	private long mStartProgress;
	private long mCurrentTime;
	private long mProgressTime;

	//Other
	protected Enemy		mLocked;
	protected boolean 	mActive;
	protected boolean	mShowRange;
	protected long		mReloadTimer;
	protected float		mGeneratorSpeedBonus;
	protected float		mGeneratorDamageBonus;
	protected ArrayList<Bullet>	mBullets;
	protected ArrayList<Generator> mGenerators;
}
